package com.zhuangjie.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhaungjie.base.conduit.TypeR;
import com.zhaungjie.base.utils.StringUtils;
import com.zhuangjie.coupon.vo.ValidCouponCount;
import com.zhuangjie.mvc.future.ContextAwareExecutor;
import com.zhuangjie.security.entity.AuthenticationTokens;
import com.zhuangjie.security.helper.JwtHelper;
import com.zhuangjie.security.provider.UserProvider;
import com.zhuangjie.user.constant.WxRegisterConstant;
import com.zhuangjie.user.dto.WeixinLoginDTO;
import com.zhuangjie.user.entity.User;
import com.zhuangjie.user.entity.UserAsset;
import com.zhuangjie.user.enums.PlatformType;
import com.zhuangjie.user.feign.CouponFeignService;
import com.zhuangjie.user.mapper.UserMapper;
import com.zhuangjie.user.service.UserAssetService;
import com.zhuangjie.user.service.UserService;
import com.zhuangjie.user.vo.AssetsStatisticsVO;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author zhuangjie
 * @since 2023-10-15
 */
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final WxMaService wxMaService;

    @Autowired
    private JwtHelper jwtHelper;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ContextAwareExecutor contextAwareExecutor;

    @Autowired
    private UserAssetService userAssetService;

    @Override
    public AuthenticationTokens wxLogin(WeixinLoginDTO wxLoginInput) throws Exception {
        WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(wxLoginInput.getCode());
        String sessionKey = session.getSessionKey();
        String openid = session.getOpenid();
        if (StringUtils.isBlank(openid)) {
            throw new RuntimeException("获取的openid为空！");
        }
        // 使用openid查看有没有此用户
        LambdaQueryWrapper<User> queryUserQueryWrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getOpenId, openid)
                .eq(User::getPlatform, PlatformType.WX);
        User sysUser = this.getOne(queryUserQueryWrapper);
        // 如果系统没有此用户，且有注册信息充足，先注册
        if (sysUser == null) {
            // 这里然后没有用户信息，但已经具备注册信息
            // 使用sessionKey + encryptedData + iv 获取用户基本信息
            WxMaUserInfo wxUserInfo = wxMaService.getUserService().getUserInfo(sessionKey, wxLoginInput.getEncryptedData(), wxLoginInput.getIv());
            if (wxUserInfo == null) {
                log.error("用户注册失败，提供的信息获取用户基本信息失败");
                throw new RuntimeException("获取不到用户的基本信息");
            }
            sysUser = new User();
            sysUser.setNickname(wxUserInfo.getNickName());
            sysUser.setPassword(WxRegisterConstant.PASSWORD);
            sysUser.setUsername(openid.toUpperCase());
            sysUser.setOpenId(session.getOpenid());
            sysUser.setPlatform(PlatformType.WX);
            sysUser.setAvatar(wxUserInfo.getAvatarUrl());
            this.baseMapper.insert(sysUser);
        }
        // 如果插入成功后，sysUser会有id
        if (sysUser == null || sysUser.getId() == null) {
            throw new RuntimeException("注册时，保存后查询出来的数据为空。");
        }
        // 到这里系统已经有用户信息了
        // 生成token,返回
        final long userId = sysUser.getId();
        AuthenticationTokens authenticationTokens = jwtHelper.loginAfterCreateToken(userConfigTokenDateKeys -> {
            HashMap<String, Object> tokenData = new HashMap<>();
            tokenData.put(userConfigTokenDateKeys.USER_ID_KEY, userId);
            return tokenData;
        });
        return authenticationTokens;
    }

    @Override
    public String getUserWXOpenId(Long userId) {
        String wxOpenId = this.baseMapper.selectUserWXOpenId(userId);
        if (wxOpenId == null) {
            throw new RuntimeException("查询不到用户的openId");
        }
        return wxOpenId;
    }

    @Override
    public AssetsStatisticsVO assetsStatistics() throws ExecutionException, InterruptedException {
        CompletableFuture<ValidCouponCount> userValidCouponCountCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return TypeR.extract(couponFeignService.validCouponCount());
            } catch (Exception e) {
                throw new RuntimeException("获取用户优惠卷统计信息失败！");
            }
        }, contextAwareExecutor);
        CompletableFuture<UserAsset> userAssetCompletableFuture = CompletableFuture.supplyAsync(() -> userAssetService.getOne(Wrappers.lambdaQuery(UserAsset.class).eq(UserAsset::getUserId, UserProvider.currentUserId())), contextAwareExecutor);


        // 等待所有并行的异常请求完成
        CompletableFuture.allOf(userValidCouponCountCompletableFuture, userAssetCompletableFuture);
        ValidCouponCount validCouponCount = userValidCouponCountCompletableFuture.get();
        UserAsset userAsset = userAssetCompletableFuture.get();


        AssetsStatisticsVO assetsStatisticsVO = new AssetsStatisticsVO();
        // 用户拥有的有效店铺优惠卷数量
        assetsStatisticsVO.setValidShopCouponCount(validCouponCount.getValidShopCouponCount());
        // 用户拥有的有效平台优惠卷数量
        assetsStatisticsVO.setValidPlatformCouponCount(validCouponCount.getValidPlatformCouponCount());
        // 积分
        assetsStatisticsVO.setPoints(userAsset == null ? 0 : userAsset.getPoints());
        // 钱包金额
        assetsStatisticsVO.setWalletAmount(userAsset == null ? BigDecimal.ZERO : userAsset.getWalletAmount());
        return assetsStatisticsVO;
    }

    @Override
    public AuthenticationTokens refreshToken(String refreshToken) throws Exception {
        // 因为用户端没有设置status字段，所以不进行账号有效性效验，直接生成token返回！
        // 1. 检查骑手状态
//        LambdaQueryWrapper<User> queryRiderIsAvailableWrapper = Wrappers.lambdaQuery(User.class)
//                .eq(User::getId, jwtHelper.getTokenOwnerId(refreshToken))
//                .ne(User::getStatus, xxxx);
//        if (this.count(queryRiderIsAvailableWrapper) == 0) {
//            throw new RuntimeException("账号已被冻结，无法刷新refreshToken!");
//        }
        return jwtHelper.refreshToken(refreshToken);
    }
}
